package auth

import (
	"path"
	"reflect"
	"runtime"
	"strconv"
	"strings"

	// "strings"

	adminModel "github.com/cn-joyconn/goadmin/models/admin"
	global "github.com/cn-joyconn/goadmin/models/global"
	adminService "github.com/cn-joyconn/goadmin/services/admin"
	"github.com/cn-joyconn/goadmin/utils"
	"github.com/cn-joyconn/goutils/array"
	"github.com/cn-joyconn/goutils/strtool"
	"github.com/gin-gonic/gin"
)

type JoyPermissionGroup struct {
	GinGroup *gin.RouterGroup
}

var PermissionGroup = &JoyPermissionGroup{}
var permissionMap = make(map[string]*[]adminModel.XAdminPermissionInfo, 0)

// var funcMap = make(map[gin.HandlerFunc][]string)
func Permission() gin.HandlerFunc {
	return func(c *gin.Context) {
		handleName := c.HandlerName()
		permissionInfos, exist := permissionMap[handleName]

		// isAjax :=false
		if exist {
			resourceMap := make(map[int][]string, 0)
			var permissions []string
			var exisit bool
			for _, permissionInfo := range *permissionInfos {
				permissions, exisit = resourceMap[permissionInfo.AppID]
				if !exisit {
					permissions = make([]string, 0)
				}
				permissions = append(permissions, permissionInfo.Permission)
				resourceMap[permissionInfo.AppID] = permissions
			}
			for appid, permissions := range resourceMap {
				resourceMap[appid] = array.RemoveDuplicateStr(permissions)
			}

			validationResultModel := getAuthPass(c, resourceMap, true)
			pass := handelPass(utils.IsAjax(c), validationResultModel, c)
			if pass {
				c.Next()
			} else {
				c.Abort()
			}
		} else {
			c.Abort()
		}

	}
}
func (group *JoyPermissionGroup) registerHandlerPermission(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) {
	name := ""
	var permissions []adminModel.XAdminPermissionInfo
	version :=  convertVersion(global.AppConf.Version)
	for _, h := range handlers {
		name = runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
		permissionValue, exsit := permissionMap[name]
		if !exsit {
			permissions = make([]adminModel.XAdminPermissionInfo, 0)
		} else {
			permissions = *permissionValue
		}
		url := group.GinGroup.BasePath()
		url = url[len(global.AppConf.ContextPath):]
		url = joinPaths(url, relativePath)
		permissions = append(permissions, adminModel.XAdminPermissionInfo{AppID: appid, Permission: permission, Url: url, IsApi: isApi, Version:version})
		permissionMap[name] = &permissions
	}
}
func (group *JoyPermissionGroup) SaveAllPermissionToDB() {
	var savePermissionMap = make(map[int]*[]adminModel.XAdminPermissionInfo, 0)
	var permissions []adminModel.XAdminPermissionInfo
	if global.AppConf.RunMode=="cluster"{
		
		version :=  convertVersion(global.AppConf.Version)
		for _, perpermissions := range permissionMap {			
			for _, p := range *perpermissions {
				permissionValue, exsit := savePermissionMap[p.AppID]
				if !exsit {
					permissions = make([]adminModel.XAdminPermissionInfo, 0)
				} else {
					permissions = *permissionValue
				}
				permissions = append(permissions,p)
				savePermissionMap[p.AppID] = &permissions
			}
		}
		perInfoService := &adminService.XAdminPermissionInfoService{}
		for appid, perpermissions := range savePermissionMap {			
			perInfoService.Updates(appid,version,*perpermissions)
		}
		
	}
	
}
func (group *JoyPermissionGroup) GetAllPermissionName(appid int) []adminModel.XAdminPermissionInfo {
	result := make([]adminModel.XAdminPermissionInfo, 0)
	if global.AppConf.RunMode=="single"{
		for _, perpermissions := range permissionMap {
			for _, p := range *perpermissions {
				if appid == p.AppID {
					result = append(result, p)
				}
			}
		}
	}else if global.AppConf.RunMode=="cluster"{
		perInfoService := &adminService.XAdminPermissionInfoService{}
		result= *(perInfoService.SelectByAppID(appid))
	}
	
	return result
}
func (group *JoyPermissionGroup) Group(relativePath string, handlers ...gin.HandlerFunc) *JoyPermissionGroup {
	// ginGroup := group.ginGroup.Group(relativePath,handlers...)
	return &JoyPermissionGroup{
		GinGroup: group.GinGroup.Group(relativePath, handlers...),
	}

}

// POST is a shortcut for router.Handle("POST", path, handle).
func (group *JoyPermissionGroup) POST(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	// fmt.Printf(ginGroup.BasePath())
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.POST(relativePath, handlers...)
}

// GET is a shortcut for router.Handle("GET", path, handle).
func (group *JoyPermissionGroup) GET(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.GET(relativePath, handlers...)
}

// DELETE is a shortcut for router.Handle("DELETE", path, handle).
func (group *JoyPermissionGroup) DELETE(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.DELETE(relativePath, handlers...)
}

// PATCH is a shortcut for router.Handle("PATCH", path, handle).
func (group *JoyPermissionGroup) PATCH(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.PATCH(relativePath, handlers...)
}

// PUT is a shortcut for router.Handle("PUT", path, handle).
func (group *JoyPermissionGroup) PUT(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.PUT(relativePath, handlers...)
}

// OPTIONS is a shortcut for router.Handle("OPTIONS", path, handle).
func (group *JoyPermissionGroup) OPTIONS(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.OPTIONS(relativePath, handlers...)
}

// HEAD is a shortcut for router.Handle("HEAD", path, handle).
func (group *JoyPermissionGroup) HEAD(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.HEAD(relativePath, handlers...)
}

// Any registers a route that matches all the HTTP methods.
// GET, POST, PUT, PATCH, HEAD, OPTIONS, DELETE, CONNECT, TRACE.
func (group *JoyPermissionGroup) Any(appid int, relativePath string, permission string, isApi bool, handlers ...gin.HandlerFunc) gin.IRoutes {
	group.registerHandlerPermission(appid, relativePath, permission, isApi, handlers...)
	return group.GinGroup.Any(relativePath, handlers...)
}

func joinPaths(absolutePath, relativePath string) string {
	if relativePath == "" {
		return absolutePath
	}

	finalPath := path.Join(absolutePath, relativePath)
	appendSlash := lastChar(relativePath) == '/' && lastChar(finalPath) != '/'
	if appendSlash {
		return finalPath + "/"
	}
	return finalPath
}

func lastChar(str string) uint8 {
	if str == "" {
		panic("The length of the string can't be 0")
	}
	return str[len(str)-1]
}

func convertVersion(version string)int64{
	var num int64	
	num = 0
	version = strings.ToLower(version)
	if strings.HasPrefix(version,"v"){
		version = version[1:]
	}	
	vers:=strings.Split(version,".")
	if vers!=nil{		
		var err error
		var n int64
		for i,ver := range vers{
			if !strtool.IsBlank(ver){
				n,err=strconv.ParseInt(ver,10,64)
				if err==nil{
					num+=(n<<i)
				}
				
			}
		}
	}
	return num
}